#ifndef PICPARAMS_H
#define PICPARAMS_H

#include <vector>
#include <string>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "../Tool/Log.h"

class ParallelMPI;
class IO;

struct SpeciesParameter
{
    std::string species_name;

    //species_type: particle, fluid
    std::string species_type;

    std::string init_position_type;

    std::string init_velocity_type;

    //coefficient  of particle max number
    double c_part_max;

    double mass;

    double charge;

    double number_density;

    //n_part_per_cell_for_weight is used to calculate particle weight
    //Now weight is the same for all the particles!!!
    //if initialize paritcles before PIC loop, the n_part_per_cell should be equal to n_part_per_cell_for_weight
    int n_part_per_cell;
    int n_part_per_cell_for_weight;

    //all particles of one species has the same weight
    //weight = nb_density / n_part_per_cell; if n_part_per_cell = 0, weight = nb_density / 100
    double weight;


    //temperature, isotropic
    double temperature;

    //temperature, anisotropic
    double temperature_x;
    double temperature_y;
    double temperature_z;

    std::vector<double> mean_velocity;

    //control timesteps, use larger timesteps for some heavy particles
    int timestep_frozen;

    //Boundary conditions for particules
    std::string bc_part_type_west;
    std::string bc_part_type_east;
    std::string bc_part_type_south;
    std::string bc_part_type_north;
    std::string bc_part_type_bottom;
    std::string bc_part_type_up;


    //=============== parameters for DSMC ===================================
    double diameter;
    //reference temperature
    double ref_temperature;
    double visc_temp_index;
    double vss_scat_inv;

    //==============Parameters for physical sputtering and backscattering============
    //atomic number: H 1; D 1; C 6; Ar 18;
    int atomic_number;

    //atomic mass: unit is amu, H 1.008; D 2.016; T 3.024; Be 9.012182; Ar 39.95
    double atomic_mass;

    //surface_binding_energy
    double surface_binding_energy;

    //density---unit-- C/A**3 ---*
    double density_solid;

    //============Parameters for backscattering as the target
    //number of constituent elements in the target.
    int ne2;

    //array for atomic numbers of the constituents.
    std::vector<int> an2_vector;

    //array for relative numbers of the constituents.
    std::vector<int> nw2_vector;

};


struct ParticleSourceParameter
{
    std::string species_name;

    std::string species_name_dependent;

    int species_number;

    int species_number_dependent;

    //particle source type
    std::string ps_type;

    std::string load_type;

    std::string emit_type;

    double number_density;

    //load particle number is fixed, load time step number is calculated
    int load_particle_number;

    //load density per second
    double dn;

    double dn_scale_down;
    
    // dq = dn (m-3) * temperature (eV)
    double dq;

    double temperature;

    std::vector<double> mean_velocity;

    std::vector<double> load_position_start;
    std::vector<double> load_position_end;

    int timestep_update;

    bool is_dependent;

    //false: add particle source only at first timestep; true: add particle source every timestep
    bool is_every_time;

    int emit_particle_number;

    double emit_flux;

    std::string emit_position;

    double emit_offset;

    //pid control parameters
    double kp;
    double ki;
    double kd;

    double heat_number_ratio;

    double load_dn_scale;

};


struct CollisionParameter
{
    std::string collision_type;

    std::vector<std::string> species_name_list;

    std::vector<int> species_number_list;

    std::vector<double> region_collision_zoom_start;

    std::vector<double> region_collision_zoom_end;

    //cross section file name
    std::string cs_file_name;

    int timestep_collision;

    double time_zoom_factor;
};

struct PSIParameter
{
    std::string psi_type;

    std::vector<std::string> species_name_list;

    std::vector<int> species_number_list;

    //position offset of injected or sputtered particles
    double position_offset;

    //if is_self_consistent is false, only calculate the psiRate (like sputtering rate or reflection rate)
    //not create new particles
    bool is_self_consistent;

    //for 1d: left or right
    std::string psi_position;

    //for recycling
    double recycling_factor;

    double recycling_temperature;

    //for sputerring


};



struct DiagnosticParameter
{
    //vdf: velocity distribution funtion
    //edf: energy distribution function
    //vedf: some parameters of vdf and edf are same

    //division number of velocity and energy
    int vedf_n_velocity;
    int vedf_n_position;
    int vedf_n_species;

    std::vector<double>         vedf_position;
    std::vector<std::string>    vedf_species_name_list;
    std::vector<int>            vedf_species_number_list;

    std::vector<double>         edf_max_energy;

};




//---------------------------------------------------------------------------------------------------------------------
//PicParams class: holds all the properties of the simulation that are read from the json input file
//---------------------------------------------------------------------------------------------------------------------
class PicParams 
{

 public:

    PicParams();


    void read_species();

    void read_particle_source();

    void read_collision();

    void read_psi();

    void read_diagnostic();


    void compute();

    int get_species_number(const std::string& species_name);

    bool get_string(std::string& cpp_value, std::string value_name);
    bool get_double(double& cpp_value, std::string value_name);
    bool get_int(int& cpp_value, std::string value_name);
    bool get_bool(bool& cpp_value, std::string value_name);

    bool get_string_vector(std::vector<std::string>& cpp_value, std::string value_name);
    bool get_double_vector(std::vector<double>& cpp_value, std::string value_name);
    bool get_int_vector(std::vector<int>& cpp_value, std::string value_name);

    bool get_struct_string(const rapidjson::Value& json_value, std::string& cpp_value, std::string value_name);
    bool get_struct_double(const rapidjson::Value& json_value, double& cpp_value, std::string value_name);
    bool get_struct_int(const rapidjson::Value& json_value, int& cpp_value, std::string value_name);
    bool get_struct_bool(const rapidjson::Value& json_value, bool& cpp_value, std::string value_name);


    bool get_struct_string_vector(const rapidjson::Value& json_value, std::vector<std::string>& cpp_value, std::string value_name);
    bool get_struct_double_vector(const rapidjson::Value& json_value, std::vector<double>& cpp_value, std::string value_name);
    bool get_struct_int_vector(const rapidjson::Value& json_value, std::vector<int>& cpp_value, std::string value_name);

    //=========== json related variables ========================
    const char* file_name = "data/parameter.inp";

    rapidjson::Document dom;


    //========== PicParams memeber variables for simulation =====

    //define the method of PIC: explicit or implicit
    std::string method;

    //define poisson equation solver: SuperLU_serial, SuperLU_mpi, petsc
    std::string solver_type;

    //parameters for petsc ksp solver
    int petsc_ksp_process_number;
    double petsc_ksp_tolerance_rtol;

    //theta of pushing particles for implicit method
    double imp_theta;

    //theta of pushing particles for implicit method
    double imp_iteration_number;


    bool is_same_weight;

    bool is_self_consistent_electric_field;

    //temperature, anisotropic
    bool is_temperature_anisotropic;

    //read data from h5 file as initial condition, or constant external field
    std::string read_data_file_name;

    //velocity due to classical or anomalous transport, along y direction
    double v_transport;

    double velocity_transport;


    //defines the geometry of the simulation
    std::string geometry;

    //defines the interpolation order
    int interpolation_order;

    //defines the projection order
    int projection_order;

    double sim_time;

    //should be global simulation size
    std::vector<double> sim_length;

    int nx_source_left;

    //Boundary conditions for PhysicalFieldetic Fields
    std::vector<std::string> bc_em_type_x;
    std::vector<std::string> bc_em_type_y;
    std::vector<std::string> bc_em_type_z;

    std::vector<double> bc_em_value_x;
    std::vector<double> bc_em_value_y;
    std::vector<double> bc_em_value_z;

    std::vector<SpeciesParameter> species_param;

    std::vector<ParticleSourceParameter> particle_source_param;

    std::vector<CollisionParameter> collision_param;

    std::vector<PSIParameter> psi_param;

    std::vector<DiagnosticParameter> diagnostic_param;

    //time step for the simulation
    double dt;

    //number of total timesteps to perform in the simulation
    int timestep_simulation;

    int timestep_io;

    int timestep_average;

    //timestep for all kind collision
    //timestep for DSMC and coulomb should be several times of this timestep
    int timestep_collision;

    //number of timestep of performing coulomb collision
    int timestep_coulomb;

    //number of timestep of performing DSMC collision
    int timestep_DSMC;

    //number of timestep of performing DSMC collision
    int timestep_restore;

    //artificially change the collision frequency
    double collision_zoom_factor;

    std::vector<double> region_collision_zoom;

    //number of space dimensions for the particle velocity
    int n_dim_particle;

    //number of space dimensions for the fields, same as the dim of particle position
    int n_dim_field;

    std::vector<int> dims_field;

    std::vector<int> dims_space;

    std::vector<int> dims_field_global;

    std::vector<int> dims_space_global;

    //spatial step (cell dimension in every direction)
    std::vector<double> cell_length;

    //inverse of spatial step (cell dimension in every direction)
    std::vector<double> cell_length_inv;

    std::vector<double> local_min; 
    std::vector<double> local_max; 

    //Oversize domain to exchange less particles
    std::vector<int> oversize;

    //cell_starting_global_index : index of 1st cell of local subdomain in the global domain
    //   - concerns ghost data
    //   - "- oversize" on rank 0
    std::vector<int> cell_starting_global_index;
    
    //continue simulation after reloading particles
    bool is_continue;

    //Number of MPI process per direction (default : as square as possible)
    std::vector<int> number_of_procs;

    //extern magnetic fields
    std::vector<double> externB;

    std::string grid_type;

    //some physics constants
    const double const_c;
    const double const_e;
    const double const_emass;
    const double const_ephi0;
    const double const_pi;
    const double const_boltz;     //Boltzmann constant
    const double const_h;         //Planck constant

};

#endif
